package com.txw.javaCore.server.service;

import com.github.rholder.retry.*;
import com.google.common.base.Predicates;
import com.txw.javaCore.model.entity.AppInfo;
import com.txw.javaCore.model.mapper.AppInfoMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @PROJECT_NAME: JavaCoreB2
 * @DESCRIPTION: Guava_Retrying重试机制
 * @Author: 涂玄武
 * @DATE: 2021/3/16 15:05
 */
@Service
@Slf4j
public class RetryService {

    @Autowired
    private AppInfoMapper appInfoMapper;

    @Autowired
    private MailService mailService;

    /**
     * 获取某个APP应用详情
     * @param id
     * @return
     */
    public AppInfo getAppInfo(final Integer id){
        Optional<AppInfo> optional = Optional.ofNullable(appInfoMapper.selectByPrimaryKey(id));
        if (optional.isPresent()){
            return optional.get();
        }else {
            //TODO:当没有查询到该条记录时，执行重试逻辑
            doRetry(id);
            return appInfoMapper.selectByPrimaryKey(id);
        }
    }

    /**
     * 执行重试逻辑
     * @param id
     */
    private void doRetry(final Integer id){

        //TODO:定义任务实例
        Callable<AppInfo> callable = () -> {
            return appInfoMapper.selectByPrimaryKey(id);
        };

        //TODO:每次重试时，监听器执行的逻辑
        RetryListener retryListener = new RetryListener() {
            @SneakyThrows
            @Override
            public <V> void onRetry(Attempt<V> attempt) {
                //重试次数
                long attemptNumber = attempt.getAttemptNumber();
                log.info("----每次重试时 监听器执行的逻辑,当前已经是第 {} 次重试了----",attemptNumber);

		        if (attemptNumber == 3){
		            log.error("--重试次数已到，是不是得该执行一些补偿逻辑，如发送短信、发送邮件...");

                    mailService.sendSimpleEmail("重试次数已到","请各位大佬上去检查一下Appinfo是否存在","707904142@qq.com");
		        }
            }
        };

        //TODO:定义重试器
        Retryer<AppInfo> retryer = RetryerBuilder.<AppInfo>newBuilder()
                //TODO:当返回结果为 false 时 - 执行重试(即sysCofig为null)
                .retryIfResult(Objects::isNull)
                //TODO:当执行核心业务逻辑抛出RuntimeException - 执行重试
                .retryIfRuntimeException()
                //TODO:还可以自定义抛出何种异常时 - 执行重试
                .retryIfExceptionOfType(IOException.class)
                //TODO:每次重试时的时间间隔为10s (当然啦，实际项目中一般是不超过1s的，如500ms，这里是为了方便模拟演示)
                .withWaitStrategy(WaitStrategies.fixedWait(10L, TimeUnit.SECONDS))
                //TODO:重试次数为3次，3次之后就不重试了
                .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                //TODO:每次重试时定义一个监听器listener，监听器的逻辑可以是 "日志记录"、"做一些补偿操作"...
                .withRetryListener(retryListener)
                .build();

        try {
            retryer.call(callable);
        } catch (ExecutionException | RetryException e) {
            e.printStackTrace();
        }
    }
}
